package com.ksyun.campus.metaserver.util;

import com.ksyun.campus.metaserver.domain.DataServerInfo;
import lombok.Data;

import java.util.*;

@Data
public class LoadBalanceUtil {
    private List<DataServerInfo> servers;

    public LoadBalanceUtil(List<DataServerInfo> servers) {
        this.servers = servers;
    }

    /**
     * 简单随机负载均衡
     *
     * @return
     */
    public DataServerInfo getRandomServer() {
        return servers.get(new Random().nextInt(servers.size()));
    }

    /**
     * 根据可使用容量比率负载均衡
     *
     * @return
     */
    public DataServerInfo getLoadBalanceServersByFreeRatio() {
        if (servers == null || servers.size() == 0) {
            return null;
        }
        servers.sort(Comparator.comparing(DataServerInfo::freeCapacityRatio).reversed());
        return servers.get(0);
    }

    /**
     * 尽可能的选不在所给机架和区域上的，不满足条件时，按可使用容量比率高的返回
     * @param num
     * @param zone
     * @param rack
     * @return
     */
    public List<DataServerInfo> getLoadBalanceServersByCondition(int num, String rack, String zone) {
        List<DataServerInfo> weightedServers = new ArrayList<>();
        if (servers != null && servers.size() <= num) {
            weightedServers.addAll(servers);
            return weightedServers;
        }

        // Sort servers by capacity ratio (higher is better)
        servers.sort(Comparator.comparing(DataServerInfo::freeCapacityRatio).reversed());

        // Select servers from different clusters and racks
        Set<String> selectedZones = new HashSet<>();
        Set<String> selectedRacks = new HashSet<>();
        if (zone!=null && zone.trim().length()!=0){
            selectedZones.add(zone);
        }
        if (rack!=null && rack.trim().length()!=0){
            selectedRacks.add(rack);
        }

        // 先按机架选
        for (DataServerInfo server : servers) {
            if (weightedServers.size() >= num) {
                break;
            }
            if (!selectedRacks.contains(server.getRack())) {
                weightedServers.add(server);
                selectedRacks.add(server.getRack());
            }
        }
        // 再按区域选
        for (DataServerInfo server : servers) {
            if (weightedServers.size() >= num) {
                break;
            }
            if (!selectedZones.contains(server.getZone()) && !selectedRacks.contains(server.getRack())) {
                weightedServers.add(server);
                selectedZones.add(server.getZone());
                selectedRacks.add(server.getRack());
            }
        }
        // 如果还不足，直接添加
        for (DataServerInfo server : servers) {
            if (weightedServers.size() >= num) {
                break;
            }
            weightedServers.add(server);
        }

        return weightedServers;
    }

    /**
     * 根据可使用容量率选择给定个数的服务器，尽量在不同机架上，不同区域， 不满足条件时， 按可使用容量比率高的返回
     *
     * @param num 需要的服务器个数
     * @return
     */
    public List<DataServerInfo> getLoadBalanceServersByFreeRatioFromDifferentDistance(int num) {
        return getLoadBalanceServersByCondition(num,null,null);
    }

}
